﻿using Microsoft.AspNetCore.Http;
using System.Text.RegularExpressions;

namespace EssentialWebFramework.Utils
{
    public class UploadFileSlice
    {
        private const string DSP_FILE_SUFFIX = "DSP";
        private const string SLICE_FILE_MIDFIX = "PART";
        private readonly static Regex dspReg = new Regex($"^(.*?)_(.*?)_(\\d+)_{ DSP_FILE_SUFFIX }$");
        private readonly static Regex sliceReg = new Regex($"_{ SLICE_FILE_MIDFIX }_(\\d+)_(\\d+)$");

        /*
            描述文件形如：lbwnb123123_938043ef610231ed413a2025f63d8ad8_117910112_DSP
            分片文件形如：lbwnb123123_asdfjzcxvMY_FILE.mp4_PART_10241_20480
         */

        /// <summary>
        /// 文件系统路径
        /// </summary>
        private string dir;
        /// <summary>
        /// 唯一标记
        /// </summary>
        private string uniqueTag;

        /// <summary>
        /// ctor
        /// </summary>
        public UploadFileSlice(string dir, string uniqueTag)
        {
            this.dir = dir;
            this.uniqueTag = uniqueTag;
        }

        /// <summary>
        /// 上传分片
        /// </summary>
        /// <param name="fileHash">最终文件的哈希（外部传入用于对比）</param>
        /// <param name="start">该片段在最终文件中起始的字节数</param>
        public string UploadSlice(IFormFile file, string fileHash, long start)
        {
            long end = start + file.Length;

            FileDSP finalDsp = this.GetDescription();
            if(finalDsp == null)
            {
                return "非法操作";
            }
            if (finalDsp.FileHash != fileHash)
            {
                return "请选择与之前相同的文件，或删除之前的文件后重新上传";
            }
            
            List<FileInfo> fileInfos = this.GetCurrentSlickFileInfos();
            List<FileSliceDSP> fileSliceDSPs = this.GetCurrentSliceDSPs(fileInfos);
            var notSeqQUery = fileSliceDSPs.Where(a => start == a.EndByte);
            if (fileSliceDSPs.Count > 0 && !notSeqQUery.Any())
            {
                return "只能顺序上传";
            }

            string thisSliceFileName = $"{ this.uniqueTag }_{ file.FileName }_{ SLICE_FILE_MIDFIX }_{ start }_{ end }";
            string thisSliceFileFullName = Path.Combine(this.dir, thisSliceFileName);

            try
            {
                using (FileStream fs = new FileStream(thisSliceFileFullName, FileMode.CreateNew))
                {
                    file.CopyTo(fs);
                }
            }
            catch (Exception ex)
            {
                new FileInfo(thisSliceFileFullName).Delete();
                return $"上传文件错误：{ ex.Message }";
            }

            fileInfos.Add(new FileInfo(thisSliceFileFullName));
            fileSliceDSPs.Add(new FileSliceDSP { StartByte = start, EndByte = end });

            long thisSum = fileSliceDSPs.Sum(dsp => dsp.EndByte - dsp.StartByte) - (fileSliceDSPs.Count > 1 ? 1 : 0);
            if(thisSum == finalDsp.FileLength)
            {
                fileInfos = fileInfos.OrderBy(a => int.Parse(sliceReg.Match(a.Name).Groups[1].ToString())).ToList();

                string targetFileName = $"{ this.uniqueTag }_{ file.FileName }";  // 最后一次时，将上传的文件名作为最终文件名
                string targetFileFullName = Path.Combine(this.dir, targetFileName);

                try
                {
                    using (FileStream fs = new FileStream(targetFileFullName, FileMode.CreateNew))
                    {
                        foreach (var fileInfo in fileInfos)
                        {
                            using (FileStream fsPart = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read))
                            {
                                fsPart.CopyTo(fs);
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    new FileInfo(thisSliceFileFullName).Delete();
                    new FileInfo(targetFileFullName).Delete();
                    return $"合并文件错误：{ ex.Message }";
                }

                // 删除分片文件
                foreach (var fileInfo in fileInfos)
                {
                    fileInfo.Delete();
                }
                // 删除描述文件
                this.DeleteDescription();

                return targetFileName;
            }
            else
            {
                return "OK";
            }
        }

        /// <summary>
        /// 获取应该上传的起始位置
        /// </summary>
        /// <param name="fileHash"></param>
        /// <returns></returns>
        public long? GetToUploadStartIndex(string fileHash)
        {
            List<FileSliceDSP> fileSliceDSPs = this.GetCurrentSliceDSPs(this.GetCurrentSlickFileInfos());
            if(fileSliceDSPs.Count == 0)
            {
                return 0;
            }
            else
            {
                var dsp = GetDescription();
                if(dsp.FileHash != fileHash)
                {
                    return null;
                }
                else
                {
                    return fileSliceDSPs.Max(a => a.EndByte);
                }                
            }            
        }

        /// <summary>
        /// 判断文件是否首次上传
        /// </summary>
        /// <returns></returns>
        public bool IsFileFirstUpload()
        {
            DirectoryInfo di = new DirectoryInfo(this.dir);
            if (!di.Exists)
            {
                di.Create();
            }

            var query = di.GetFiles().Where(file => file.Name.StartsWith(this.uniqueTag) && dspReg.IsMatch(file.Name));
            return !query.Any();
        }

        /// <summary>
        /// 设置描述文件（空文件，文件名即信息）
        /// </summary>
        /// <param name="fileHash"></param>
        /// <param name="fileLength"></param>
        public void SetDescriptionFile(string fileHash, long fileLength)
        {
            string fileName = $"{ this.uniqueTag }_{ fileHash }_{ fileLength }_{ DSP_FILE_SUFFIX }";
            FileInfo fi = new FileInfo(Path.Combine(this.dir, fileName));
            if (!fi.Exists)
            {
                fi.Create().Close();
            }
        }

        /// <summary>
        /// 获取描述文件
        /// </summary>
        /// <returns></returns>
        private FileDSP GetDescription()
        {
            FileInfo fi = new DirectoryInfo(this.dir).GetFiles()
                                .Where(file => file.Name.StartsWith(this.uniqueTag) && dspReg.IsMatch(file.Name))
                                .FirstOrDefault();
            if (fi == null)
            {
                return null;
            }

            MatchCollection mc = dspReg.Matches(fi.Name);
            return new FileDSP 
            { 
                FileHash = mc[0].Groups[2].ToString(), 
                FileLength = long.Parse(mc[0].Groups[3].ToString()) 
            };            
        }

        /// <summary>
        /// 删除描述文件
        /// </summary>
        private void DeleteDescription()
        {
            FileInfo fi = new DirectoryInfo(this.dir).GetFiles()
                                .Where(file => file.Name.StartsWith(this.uniqueTag) && dspReg.IsMatch(file.Name))
                                .FirstOrDefault();
            if (fi != null && fi.Exists)
            {
                fi.Delete();
            }
        }

        /// <summary>
        /// 获取当前所有分片的 FileInfo
        /// </summary>
        /// <returns></returns>
        private List<FileInfo> GetCurrentSlickFileInfos()
        {
            DirectoryInfo di = new DirectoryInfo(this.dir);
            if (di.Exists)
            {
                List<FileInfo> fileInfos = new DirectoryInfo(this.dir).GetFiles()
                                        .Where(file => file.Name.StartsWith(this.uniqueTag)
                                                        && sliceReg.IsMatch(file.Name)).ToList();
                return fileInfos;
            }
            else
            {
                return new List<FileInfo>();
            }
            
        }

        /// <summary>
        /// 获取当前所有分片的情况（起始位置和结束位置）
        /// </summary>
        /// <returns></returns>
        private List<FileSliceDSP> GetCurrentSliceDSPs(List<FileInfo> fileInfos)
        {
            List<FileSliceDSP> fileSliceDSPs = fileInfos.Select(file =>
            {
                Match match = sliceReg.Match(file.Name);
                return new FileSliceDSP
                {
                    StartByte = long.Parse(match.Groups[1].ToString()),
                    EndByte = long.Parse(match.Groups[2].ToString())
                };
            }).OrderBy(a => a.StartByte).ToList();

            return fileSliceDSPs;
        }

        /// <summary>
        /// 文件描述
        /// </summary>
        public class FileDSP
        {
            public string FileHash { get; set; }
            public long FileLength { get; set; }
        }

        /// <summary>
        /// 文件分片的描述
        /// </summary>
        public class FileSliceDSP
        {
            /// <summary>
            /// 开始字节（闭区间）
            /// </summary>
            public long StartByte { get; set; }
            /// <summary>
            /// 结束字节（开区间）
            /// </summary>
            public long EndByte { get; set; }
        }
    }
}
